/*
 * File:    Server.java
 * Created: 16-May-2007
 * Version: $Id$
 *
 * COPYRIGHT (C) 2007, Bitgate Software, LLC.  All Rights Reserved.
 *
 * software@bitgatesoftware.com
 */

package com.bitgate.util.service;

import static com.bitgate.util.debug.Debug.crit;
import static com.bitgate.util.debug.Debug.debug;
import static com.bitgate.util.debug.Debug.info;
import static com.bitgate.util.debug.Debug.isDebugEnabled;

import java.io.File;

import org.w3c.dom.Node;

import com.bitgate.util.config.Config;
import com.bitgate.util.config.Constants;
import com.bitgate.util.scheduler.Scheduler;
import com.bitgate.util.service.protocol.ServerContext;

public class Server
{
	public static final String VERSION = "0.8.2";
	
	public static void main(String args[])
	{
		int runningServices = 0;
		
		System.err.println("--- Bitgate Server Software Environment version " + VERSION);
		System.err.println("--- COPYRIGHT (C) 2001-2006, Nuklees Software, LLC.  All Rights Reserved.");
		System.err.println("--- COPYRIGHT (C) 2006-2008, Bitgate Software, LLC.  All Rights Reserved.");
		System.err.println("--- COPYRIGHT (C) 2008-2009, Bitgate Mobile, LLC.  All Rights Reserved.");
		System.err.println("--- RELEASED UNDER THE CREATIVE COMMONS SHAREALIKE 2.5/3.0 LICENSE\n");
		
		// Start system service: Scheduler.
		(new Thread(Scheduler.getDefault(), "Task Scheduler Thread")).start();
		
		String servicesFilename = System.getProperty("services.config");
		
		if (servicesFilename == null) {
			servicesFilename = "conf/defaults/services.xml";
		}
		
		// Load in the system services file, and begin parsing through it to start the services necessary to run
		// the server.
		Config.getDefault().register(servicesFilename, "services");
		
		int numObjects = Integer.parseInt(Config.getDefault().parseConfig("services", "count(" + Constants.SERVICES_OBJECT.get() +
				")"));
		
		if (numObjects == 0) {
			crit("No valid services have been defined in the services.xml file.");
			crit("At least one service needs to be enabled before the server can start.");
			System.exit(-1);
		}
	        
		for(int i = 1; i < (numObjects + 1); i++) {
			String serviceType = Config.getDefault().parseConfig("services", Constants.SERVICES_OBJECT.get(i) + "/@type");
			String serviceName = Config.getDefault().parseConfig("services", Constants.SERVICES_OBJECT.get(i) + "/@name");
			String serviceEnabled = Config.getDefault().parseConfig("services", Constants.SERVICES_OBJECT.get(i) + "/@enabled");
			Node node = Config.getDefault().getNode("services", Constants.SERVICES_OBJECT.get(i));
	        
			if (!("tuning".equalsIgnoreCase(serviceType))) {
				String serviceBindAddress = Config.getDefault().parseConfig("services",
						Constants.SERVICES_OBJECT.get(i) + "/property[@name='service.bind-address']/@value");
				String serviceBindPort = Config.getDefault().parseConfig("services",
						Constants.SERVICES_OBJECT.get(i) + "/property[@name='service.bind-port']/@value");
				int serviceBind = 0;
				boolean serviceSecure = false;
				
				ServerContext.getDefault().addServerContext(serviceName, node, serviceBindAddress,
					Integer.parseInt(serviceBindPort));
				
				if (serviceEnabled != null) {
					if ("false".equalsIgnoreCase(serviceEnabled)) {
						info("Skipping instantiation of service '" + serviceName + "': administratively disabled.");	
						continue;
					}
				}
				
				if (serviceBindAddress == null || "".equals(serviceBindAddress)) {
					serviceBindAddress = "*";
				}
				
				if (serviceBindPort == null || "".equals(serviceBindPort)) {
					if ("http".equalsIgnoreCase(serviceType)) {
						serviceBind = 80;
					} else if ("https".equalsIgnoreCase(serviceType)) {
						serviceBind = 443;
					}
				} else {
					serviceBind = Integer.parseInt(serviceBindPort);
				}
				
				if ("https".equalsIgnoreCase(serviceType)) {
					serviceSecure = true;
				}
				
				Service service = null;
				ServiceContext sContext = new ServiceContext(serviceBind, serviceSecure, serviceType);
				
				if (serviceSecure) {
					String serviceKeyFile = Config.getDefault().parseConfig("services",
							Constants.SERVICES_OBJECT.get(i) + "/property[@name='service.key-file']/@value");
					String serviceKeyPassword = Config.getDefault().parseConfig("services",
							Constants.SERVICES_OBJECT.get(i) + "/property[@name='service.key-password']/@value");
					String serviceStorePassword = Config.getDefault().parseConfig("services",
							Constants.SERVICES_OBJECT.get(i) + "/property[@name='service.store-password']/@value");
					String serviceSslContext = Config.getDefault().parseConfig("services",
							Constants.SERVICES_OBJECT.get(i) + "/property[@name='service.ssl-context']/@value");
					String serviceKeyInstance = Config.getDefault().parseConfig("services",
							Constants.SERVICES_OBJECT.get(i) + "/property[@name='service.key-instance']/@value");
	                
					if ((new File(serviceKeyFile)).exists()) {
						sContext.setKeyInstance(serviceKeyInstance);
						sContext.setKeyFile(serviceKeyFile);
						sContext.setStorePassword(serviceStorePassword);
						sContext.setKeyPassword(serviceKeyPassword);
						sContext.setSslContext(serviceSslContext);
					} else {
						crit("Key file '" + serviceKeyFile + "' in secure service '" + serviceName + "' either does not " +
								"exist or cannot be accessed.  This service will not be started.");
						
						continue;
					}
				}
	        
				sContext.setName(serviceName);
	        
				try {
					service = new Service(sContext);
					ServiceCache.getDefault().addService(serviceName, service);
				} catch(ServiceException e) {
					if (isDebugEnabled()) {
						debug("Unable to start service '" + serviceName + "': " + e.getMessage() + ": Skipped.");
					}
					
					crit("Unable to start service '" + serviceName + "': " + e.getMessage());
					
					continue;
				}
				
				(new Thread(service, "Server: Type='" + serviceType + "' SID='" + serviceName + "' Secure='" + serviceSecure +
					"'")).start();
				
				runningServices++;
			}
		}
		
		if (runningServices == 0) {
			crit("Unable to start any services.  Shutting down.");
			System.exit(-1);
		} else {
			debug("Services running: " + runningServices);
		}
	}

}